package bma.common.netty.telnetserver;

import java.nio.charset.Charset;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;

import bma.common.langutil.core.ExceptionUtil;
import bma.common.langutil.core.ValueUtil;
import bma.common.langutil.jmshell.JMShellProcessor;
import bma.common.netty.NettyServer;
import bma.common.netty.telnetserver.common.CommonTelnetSession;

public class NettyTelnetServer extends NettyServer {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(NettyTelnetServer.class);

	private String charset;
	private int maxTextLength = 1024;
	private JMShellProcessor processor;

	public NettyTelnetServer() {
		super();
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public int getMaxTextLength() {
		return maxTextLength;
	}

	public void setMaxTextLength(int maxTextLength) {
		this.maxTextLength = maxTextLength;
	}

	public JMShellProcessor getProcessor() {
		return processor;
	}

	public void setProcessor(JMShellProcessor processor) {
		this.processor = processor;
	}

	@Override
	protected void beforeBuildPipeline(ChannelPipeline pipeline) {
		super.beforeBuildPipeline(pipeline);
		Charset ch = Charset.defaultCharset();
		if (ValueUtil.notEmpty(charset)) {
			ch = Charset.forName(charset);
		}
		pipeline.addLast("frame", new DelimiterBasedFrameDecoder(maxTextLength,
				Delimiters.lineDelimiter()));
		pipeline.addLast("decoder", new StringDecoder(ch));
		pipeline.addLast("encode", new StringEncoder(ch));
		pipeline.addLast("main", new Handler());
	}

	public SessionRealm createSession(Channel ch) {
		String id = getName() + "_" + ch.getId();
		return new CommonTelnetSession(id, ch) {
			@Override
			public void execute(String command) {
				if (processor == null) {
					this.writeln("ERROR:no processor");
					return;
				}
				processor.process(this, command);
			}			
		};
	}

	public void closeSession(Channel ch, SessionRealm s) {
		if (s != null) {
			s.onClose();
		}
	}

	public class Handler extends SimpleChannelUpstreamHandler {

		SessionRealm session;

		@Override
		public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
				throws Exception {
			super.channelOpen(ctx, e);
			session = createSession(ctx.getChannel());
		}

		@Override
		public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
				throws Exception {
			if (session != null) {
				closeSession(ctx.getChannel(), session);
				session = null;
			}
			super.channelClosed(ctx, e);
		}

		@Override
		public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
				throws Exception {
			String text = (String) e.getMessage();
			session.execute(text);
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext nctx, ExceptionEvent e)
				throws Exception {
			Channel ch = e.getChannel();
			Throwable cause = e.getCause();
			if (log.isWarnEnabled()) {
				log.warn("process request fail", cause);
			}
			if (ch.isOpen()) {
				String s = ExceptionUtil.toStackTraceString(cause);
				ch.write(s);
			}
		}

	}
}
